package main;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.TimerTask;

import javax.swing.*;

import spells.*;
import creatures.*;


public class BattleGrid extends JFrame implements ActionListener, KeyListener{

	/* Initialize main variables used by GridTest, including Grid used, active player, active enemy and test variables
	 * Set position of starting characters
	 */
	Grid currentGrid;
	Player you;
	double timeUntilNextAct = 0;//this stays probably
	double eTimeUntilNextAct = 0;//this stays probably
	//combat variables (some for testing only)
	//Creature enemy;
	Timer gridTimer;
	Cell currentCell;//contains what cell has last been clicked
	//all becomes Character.health, Character.strength etc
	String tile;
	
	BattleGrid next;//next map to go to
	
	int winType;//determines what win conditions to check
	
	int xWin=-1;//x square for winner
	int yWin=-1;//y square for winner
	
	Enemy boss;//enemy boss
	
	int floor;//what floor this is
	
	//
	//contains list of creatures for multiple enemies
	ArrayList<Creature> creatures;
	//panel for drawing images
	TestingPanel currentPanel;
	
	static int ROWS = 12;
	static int COLUMNS = 16;
	//array labels for icons - uses special ImageLabel class instead of JLabel to store coordinates
	ImageLabel [][] labels = new ImageLabel[ROWS][COLUMNS];
	static int WIDTH = 824;
	static int HEIGHT = 646;
	int weaponDamage;
	double attackSpeed;
	
	public BattleGrid (Player p, ArrayList<Creature> c, String t, BattleGrid n, int w, int f){
		winType=w;
		floor=f;
		
		//DALTON - initialize player, enemy health
		you=p;
		creatures=c;
		currentCell=you.host;
		tile=t;
		next=n;
		weaponDamage = you.weapon1.damage;
		attackSpeed = you.weapon1.attackSpeed;
		
		//note by ben: 8 = rows, 6 = columns
		Grid grid1 = new Grid(COLUMNS,ROWS);
		
		currentGrid = grid1;
		//place occupants in Grid - is also needed in addition to characters grid method

		grid1.putOccupant(you.x, you.y, you);
		
		for(Occupant o:creatures){
			grid1.putOccupant(o.x, o.y, o);
		}//end for
		
		Dimension d = new Dimension (WIDTH,HEIGHT);
		this.setSize(d);
		/*this.setMaximumSize(d);
        this.setMinimumSize(d);*/
		this.setLayout(new GridLayout(1,1));
		currentPanel = new TestingPanel();
		add(currentPanel);
		
		this.setVisible(true);
		this.setResizable(false);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.addKeyListener(this);
	
		
		//timer for repainting of images - also can slow user input down if interval is bad; could use improvement
		
		gridTimer = new Timer(20,new ActionListener() {
			  // This is how you define an anonymous inner class
			  public void actionPerformed(ActionEvent e) {
			    if (creatures.size() > 0 && !you.checkDeath())
			    	combat(creatures);
			  }//end method
			});//end ActionListener
		gridTimer.start();
	}//end constructor
	
	//This will be used for square wins
	public BattleGrid (Player p, ArrayList<Creature> c, String t, BattleGrid n, int w, int x, int y, int f){
		winType=2;
		xWin=x;
		yWin=y;
		floor=f;
		
		//DALTON - initialize player, enemy health
		you=p;
		creatures=c;
		currentCell=you.host;
		tile=t;
		next=n;
		weaponDamage = you.weapon1.damage;
		attackSpeed = you.weapon1.attackSpeed;
		
		//note by ben: 8 = rows, 6 = columns
		Grid grid1 = new Grid(COLUMNS,ROWS);
		
		currentGrid = grid1;
		//place occupants in Grid - is also needed in addition to characters grid method

		grid1.putOccupant(you.x, you.y, you);
		
		for(Occupant o:creatures){
			grid1.putOccupant(o.x, o.y, o);
		}//end for
		
		Dimension d = new Dimension (WIDTH,HEIGHT);
		this.setSize(d);
		/*this.setMaximumSize(d);
        this.setMinimumSize(d);*/
		this.setLayout(new GridLayout(1,1));
		currentPanel = new TestingPanel();
		add(currentPanel);
		
		this.setVisible(true);
		this.setResizable(false);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.addKeyListener(this);
	
		
		//timer for repainting of images - also can slow user input down if interval is bad; could use improvement
		
		gridTimer = new Timer(20,new ActionListener() {
			  // This is how you define an anonymous inner class
			  public void actionPerformed(ActionEvent e) {
			    if (!checkWin())
			    	combat(creatures);
			    else
			    	System.out.println("You've won");
			  }//end method
			});//end ActionListener
		gridTimer.start();
	}//end constructor
	
	//Will be used for boss wins
	public BattleGrid (Player p, ArrayList<Creature> c, String t, BattleGrid n, int w, Enemy b, int f){
		winType=3;
		boss=b;
		floor=f;
		
		//DALTON - initialize player, enemy health
		you=p;
		creatures=c;
		currentCell=you.host;
		tile=t;
		next=n;
		weaponDamage = you.weapon1.damage;
		attackSpeed = you.weapon1.attackSpeed;
		
		//note by ben: 8 = rows, 6 = columns
		Grid grid1 = new Grid(COLUMNS,ROWS);
		
		currentGrid = grid1;
		//place occupants in Grid - is also needed in addition to characters grid method

		grid1.putOccupant(you.x, you.y, you);
		
		for(Occupant o:creatures){
			grid1.putOccupant(o.x, o.y, o);
		}//end for
		
		Dimension d = new Dimension (WIDTH,HEIGHT);
		this.setSize(d);
		/*this.setMaximumSize(d);
        this.setMinimumSize(d);*/
		this.setLayout(new GridLayout(1,1));
		currentPanel = new TestingPanel();
		add(currentPanel);
		
		this.setVisible(true);
		this.setResizable(false);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.addKeyListener(this);
	
		
		//timer for repainting of images - also can slow user input down if interval is bad; could use improvement
		
		gridTimer = new Timer(20,new ActionListener() {
			  // This is how you define an anonymous inner class
			  public void actionPerformed(ActionEvent e) {
			    if (!checkWin())
			    	combat(creatures);
			    else{
			    //	System.out.println("You've won!");
			    }//end else
			  }//end method
			});//end ActionListener
		gridTimer.start();
	}//end constructor
	
	
	/*
	 * Need to synchronize combat and keylisteners so we don't end up with race conditions -Daniel
	 */
	
	//Dalton/Richard - method called when creatures exist in grid - manages turns and combat between main character
	//and enemies, takes ArrayList of enemies as parameter
	public void combat(ArrayList <Creature> creatures){
		boolean winFlag=false;
		//Creature enemy = creatures.get(0);
		for(int i=0;i<creatures.size() && !winFlag;i++){
			//all becomes Character.health, Character.strength etc		
			Enemy enemy=(Enemy)creatures.get(i);
			int flag = 0;
			int damage = 0;
				if (flag == 0 && creatures.size()!=0)
				{
					if (timeUntilNextAct <= 0) //USER ACTION
					{
						
					}//end if
					//other actions can include: spells, MOVING (should take a set time, ex: 0.5)
		
					else if (enemy.eTimeUntilNextAct <= 0)//ENEMY ACTION. must make work for multi enemies later
					{
						if (currentGrid.getDToEnemy(enemy) > 1) {
							enemy.ai(enemy, you);
							enemy.eTimeUntilNextAct += 0.5;//needs to be moved into enemy
						}//end if
						else {
							damage = Player.attackDamage(enemy.strength,enemy.weapon1.damage,enemy.level,you.armor);
							System.out.println("Enemy attacks you for " + damage + " damage.");
							you.curHealth = you.curHealth - damage;
							System.out.println("Your Health is now: " + you.curHealth + "/" + you.maxHealth);
							enemy.eTimeUntilNextAct = enemy.eTimeUntilNextAct + enemy.weapon1.attackSpeed;
						}//end else
					}//right now only chooses to attack, enemy may use other actions later.
					//other actions can include: spells, MOVING (should take a set time, ex: 0.5)
					//AI: move towards player if player is not adjacent
					else
					{
						//reason for this division by creature size is that the player gets an action check (including minus on time until next) every time a
						//creature acts. This was why only one creature was acting at a time.
						timeUntilNextAct = timeUntilNextAct - (0.1/creatures.size());
						enemy.eTimeUntilNextAct = enemy.eTimeUntilNextAct - 0.1;
						
						for (int n=0; n < you.buffs.size(); n++)
						{
							
							
							if (you.buffs.get(n) instanceof DoT)
							{
								DoT temp = (DoT)(you.buffs.get(n));
								if (temp.getTimeSinceTick() >= temp.getTickTime())
								{
									//DO THE DAMAGE
									you.changeHealth(temp.getDamage());
									temp.displayChange(you.name);
									you.displayHealth();
									//System.out.print(you.name + " changes " + temp.getDamage() + " damage from " + temp.getName());
									temp.setTimeSinceTick(temp.getTimeSinceTick()-temp.getTickTime());
								}
								
								temp.setTimeSinceTick(temp.getTimeSinceTick() + (0.1/creatures.size()));
								
								//EXPERIMENTAL
								temp.setDuration(temp.getDuration() - (0.1/creatures.size()));
								
								if (temp.getDuration() <= 0)
								{
									System.out.println(you.buffs.get(n).getName() + " duration ends.");
									you.buffs.remove(n);
									
								}
								//removing buff after its gone
							}
							else
							{
								StatusEffect temp = you.buffs.get(n);
								temp.setDuration(temp.getDuration() - (0.1/creatures.size()));
								
								if (temp.getDuration() <= 0)
								{
									System.out.println(you.buffs.get(n).getName() + " duration ends.");
									if (you.buffs.get(n) instanceof StatChange)
									{
										((StatChange)(you.buffs.get(n))).removeBuff(you);
									}
									you.buffs.remove(n);
									
								}
							}
						}//checks DoT ticks for player
						
						for (int n=0; n < enemy.buffs.size(); n++)
						{
							if (enemy.buffs.get(n) instanceof DoT)
							{
								DoT temp = (DoT)(enemy.buffs.get(n));
								if (temp.getTimeSinceTick() >= temp.getTickTime())
								{
									//DO THE DAMAGE
									//enemy.curHealth+= temp.getDamage();
									//System.out.println(enemy.name + " changes " + temp.getDamage() + " damage from " + temp.getName());
									enemy.changeHealth(temp.getDamage());
									temp.displayChange(enemy.name);
									temp.setTimeSinceTick(temp.getTimeSinceTick()-temp.getTickTime());
									
									
									//boolean flag2=wolf.checkDeath();
									//if(flag2){
									//	System.out.println(wolf.name+" has died");
									//	you.gainXP(((Enemy)wolf).experience, ((Enemy)wolf).level);
									//	creatures.remove(wolf);
									//	currentCell.occ1=null;
									//}
								}
								
								temp.setTimeSinceTick(temp.getTimeSinceTick() + 0.1);
								
								//EXPERIMENTAL
								temp.setDuration(temp.getDuration() - 0.1);
								
								if (temp.getDuration() <= 0)
								{
									System.out.println(enemy.buffs.get(n).getName() + " duration ends.");
									enemy.buffs.remove(n);
									
								}
								//removing buff after its gone
							}
							else
							{
								StatusEffect temp = enemy.buffs.get(n);
								temp.setDuration(temp.getDuration() - (0.1/creatures.size()));
								
								if (temp.getDuration() <= 0)
								{
									System.out.println(enemy.buffs.get(n).getName() + " duration ends.");
									if (enemy.buffs.get(n) instanceof StatChange)
									{
										((StatChange)(enemy.buffs.get(n))).removeBuff(enemy);
									}
									enemy.buffs.remove(n);
									
								}
							}
							
							
						}//Make it work for n number of creatures eventually.
						
						
						
					}//end else
		
					if (you.curHealth <= 0 && flag!=1)
					{
						System.out.println("You have died!");
						flag = 1;
					}//end if
				
					boolean deathFlag=enemy.checkDeath();
					if(deathFlag){
						System.out.println(enemy.name+" has died");
						you.gainXP(enemy.experience, enemy.level);
						creatures.remove(enemy);
						if(enemy==boss)
							boss=null;
						flag=1;
						enemy=null;
					//	wolf=null;
					}//end if
					if(checkWin() && flag!=1){
						if(!winFlag)
							System.out.println("You've won");
						winFlag=true;
					}//end if
					currentPanel.fillOccImages();
			}//end if
		}//end for
	}//end method

	//Richard: controls creature commands - how creature will follow human around the grid.  Method called
	//in combat method

	//this checks to see if a user has won
	public boolean checkWin(){
		boolean saved=false;
		if(winType==1){//kill all creatures
			if(creatures.size()==0){
				save();
				return true;
			}//end if
			else
				return false;
		}//end if
		else if(winType==2){//reach a certain square
			if(you.x==xWin && you.y==yWin){
				save();
				return true;
			}//end if
			else
				return false;
		}//end else if
		else if(winType==3){//kill a boss
			if(boss==null){
				save();
				return true;
			}//end if
			else
				return false;
		}//end else if
		return false;
	}//end method

	//save character data
	public boolean save(){
		try{
			BufferedWriter outputStream=new BufferedWriter(new FileWriter("save.txt"));
			String out;
			if(next!=null)
				 out=(floor+1)+"\n"+you;
			else
				 out=floor+"\n"+you;
			outputStream.write(out);
			outputStream.flush();
			return true;
		}//end try
		catch(IOException e){
			return false;
		}
	}//end method
	
	//controls user input - user input only works if it is the user's turn, problems MAY occur due to thread conflicts
	public void keyPressed(KeyEvent e) 
	{
		int code = e.getKeyCode();
		System.out.println(KeyEvent.VK_F);
		System.out.println(code);
		synchronized(this){
			if (code == KeyEvent.VK_UP || code == KeyEvent.VK_DOWN || code == KeyEvent.VK_LEFT || code == KeyEvent.VK_RIGHT || code == KeyEvent.VK_A || code==KeyEvent.VK_1 || code==KeyEvent.VK_2 || code==KeyEvent.VK_3 || code==KeyEvent.VK_4 || code==KeyEvent.VK_5 || code==KeyEvent.VK_6 || code==KeyEvent.VK_7 || code==KeyEvent.VK_8 || code==KeyEvent.VK_9 || code==KeyEvent.VK_0){
				if (timeUntilNextAct <= 0){
					if (code == KeyEvent.VK_UP)
					{
						currentGrid.getPC().moveUp();
						timeUntilNextAct += 0.5;
					}//end else if
					else if (code == KeyEvent.VK_DOWN)
					{
						currentGrid.getPC().moveDown();
						timeUntilNextAct += 0.5;
					}//end else if
					else if (code == KeyEvent.VK_LEFT)
					{
						currentGrid.getPC().moveLeft();
						timeUntilNextAct += 0.5;
					}//end else if
					else if (code == KeyEvent.VK_RIGHT)
					{
						currentGrid.getPC().moveRight();
						timeUntilNextAct += 0.5;
					}//end else if
					else if(code==KeyEvent.VK_1){
						//System.out.println("REACHED;");
						Spell temp=you.findSpell("Fireball");
						temp.setTarget((Creature) currentCell.getOccupant());
						String cast = temp.applySpell(currentGrid);
						temp.setTarget(null);
						timeUntilNextAct=timeUntilNextAct+temp.getCastTime();
						System.out.println(cast);
					}//end else if
				
				else if(code==KeyEvent.VK_2){
					Spell temp=you.findSpell("Heal");
					String cast = temp.applySpell(currentGrid);
					System.out.println(cast);
					timeUntilNextAct=timeUntilNextAct+temp.getCastTime();
					System.out.println(you.name+"'s health is now "+you.curHealth+"/"+you.maxHealth);
				}//end else if
				else if(code==KeyEvent.VK_3){
					Spell temp=you.findSpell("Ignite");
					temp.setTarget((Creature) currentCell.getOccupant());
					String cast = temp.applySpell(currentGrid);
					temp.setTarget(null);
					timeUntilNextAct = timeUntilNextAct + temp.getCastTime();
					System.out.println(cast);
				}//end else if
				else if(code==KeyEvent.VK_4){
					Spell temp=you.findSpell("Replenish");
					temp.setTarget((Creature) currentCell.getOccupant());
					String cast = temp.applySpell(currentGrid);
					temp.setTarget(null);
					timeUntilNextAct = timeUntilNextAct + temp.getCastTime();
					System.out.println(cast);
				}//end else if
				else if(code==KeyEvent.VK_5){
					Spell temp=you.findSpell("Pain Spike");
					temp.setTarget((Creature) currentCell.getOccupant());
					String cast = temp.applySpell(currentGrid);
					temp.setTarget(null);
					timeUntilNextAct = timeUntilNextAct + temp.getCastTime();
					System.out.println(cast);
				}//end else if
				else if (code == KeyEvent.VK_A){
					//Enemy temp=(Enemy)currentCell.occ1;
					if (!(currentCell.getOccupant() instanceof Enemy)){
						JOptionPane.showMessageDialog(null, "Sorry, but selected cell contains not an enemy.");
						gridTimer.restart();
					}//end if
					else{
						Enemy temp=(Enemy)currentCell.getOccupant();
						if (currentGrid.getDToEnemy(temp) <= 1){
							int damage = Player.attackDamage(you.strength,weaponDamage,you.level,temp.armor);
							System.out.println("You attack enemy for " + damage + " damage.");
							temp.takeDamage(damage);
							System.out.println("Enemy Health is now: " + temp.curHealth + "/" + temp.maxHealth);
							timeUntilNextAct = timeUntilNextAct + attackSpeed;
						}//end if
					}//end else
				}//end else if
		//		currentPanel.fillOccImages();
	
				}//end if
				if(checkWin())
					System.out.println("You've won");
			}//end if
		}//end synchronized
	}//end if key is valid
	
	public void keyReleased(KeyEvent e) {
	}//end method

	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub

	}//end method
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub

	}//end method

	public class TestingPanel extends JPanel implements MouseListener{
		
		public TestingPanel(){
			//note by ben: GridLayout(x,y) where x = rows, y = columns
			this.setLayout(new GridLayout(ROWS,COLUMNS));
			Dimension d = new Dimension (WIDTH,HEIGHT);
			this.setSize(d);
			this.setMaximumSize(d);
			this.setMinimumSize(d);
			Dimension label = new Dimension(50,50);
			fillOccImages();
		}//end class
		
		//assumes all occupants have images assigned - fills in labels with appropriate images and combines 
		//terrain images with creature images
		public void fillOccImages(){
			//row (i)
			for (int i=ROWS-1;i>=0;i--){
				//column (j) 
				for (int j=0;j<COLUMNS;j++){
					
					//different things if there is a creature image or not in the current cell
					if (currentGrid.cellGrid[j][i].hasOccupant()){
						BufferedImage cellImage = new BufferedImage(50,50,BufferedImage.TYPE_INT_ARGB);
						Graphics2D stuff = (Graphics2D)cellImage.getGraphics();
						MediaTracker track = new MediaTracker(this);
						String occImgString = currentGrid.cellGrid[j][i].getOccImage();
					
						Image occImage = Toolkit.getDefaultToolkit().getImage(occImgString);
						track.addImage(occImage, 1);
						Image terrImage = Toolkit.getDefaultToolkit().getImage(tile);
						track.addImage(terrImage, 0);


						try {
							track.waitForAll();
						}//end try
						catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}//end catch
						stuff.drawImage(terrImage,0,0,null);
						stuff.drawImage(occImage,0,0,null);
						if (labels[i][j] == null)
							labels[i][j] = new ImageLabel(i+","+j, new ImageIcon (cellImage), j, i);
						else
							labels[i][j].setIcon(new ImageIcon (cellImage));
					}//end if
					else {
						BufferedImage cellImage = new BufferedImage(50,50,BufferedImage.TYPE_INT_ARGB);
						Graphics2D stuff = (Graphics2D)cellImage.getGraphics();
						MediaTracker track = new MediaTracker(this);
						
						Image terrImage = Toolkit.getDefaultToolkit().getImage(tile);
						track.addImage(terrImage, 0);
						try {
							track.waitForAll();
						}//end try
						catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}//end catch
						stuff.drawImage(terrImage,0,0,null);
						//if label never initialized create new imagelabel, else simply replace imagelabel image with terrain
						if (labels[i][j] == null)
							labels[i][j] = new ImageLabel(i+","+j, new ImageIcon (cellImage), j,i);
						else {
							labels[i][j].setIcon(new ImageIcon (cellImage));
							labels[i][j].setCoords(j, i);
						}//end else
					}//end else

				}//end for
			}//end for
			setLabelsToPanel();
		}//end method
		
		//places imagelabels from array into JPanel and adds mouselistener - ensures only one mouselistener is added to avoid
		//problems
		public void setLabelsToPanel(){
			this.removeAll();
			for (int i=ROWS-1;i>=0;i--){
				//column
				for (int j=0;j<COLUMNS;j++){
					add(labels[i][j]);
					if (labels[i][j].getMouseListeners().length < 1)
					labels[i][j].addMouseListener(this);
				}//end for
			}//end for
		}//end method
	
		public void mouseClicked(MouseEvent e) {
			ImageLabel i = (ImageLabel)e.getSource();
			
			currentCell=currentGrid.cellGrid[i.x][i.y];
			
			System.out.println("this is the pos of the label u clickxted: x:" + i.x + " y:" + i.y);
		}//end method
		
		public void mouseEntered(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}//end method
		
		public void mouseExited(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}//end method
		
		public void mousePressed(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}//end method
		
		public void mouseReleased(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}//end method
	}//end class
	
	public class ConsolePanel extends JPanel{
		/* To be continued...
		 * 
		 */
		//		JTextField consoleTextArea = new JTextArea();
//		consoleTextArea.
	}
	/* 
	 * Special labels with icons
	 */
	
	public class ImageLabel extends JLabel{
		int x;
		int y;
		public ImageLabel(String s, ImageIcon image, int x, int y){
			Dimension label = new Dimension(50,50);
			setMinimumSize(label);
			setMaximumSize(label);
			setSize(label);
			this.x = x;
			this.y = y;
			//setText(s);
			this.setIcon(image);
			//this.setText(labelT);
		}//end constructor
		public void setCoords(int x, int y){
			this.x = x; 
			this.y = y;
		}//end method
	}//end class

}//end class
